﻿namespace SolidMvvmLite {
	using System.ComponentModel;

	public class ViewHelper<TView, TViewModel> where TViewModel : ViewModelBase where TView : FrameworkElement, IView, IViewModelObserver {
		public ViewHelper(TView view) {
			View = view;

			view.DataContextChanged += View_DataContextChanged;
		}

		public TView View { get; }

		public TViewModel ViewModel { get; private set; }

		//public void Dispose() {
		//	GC.SuppressFinalize(this);

		//	if (ViewModel != null)
		//		Debugging.Break(); // Oops. Only dispose the ViewHelper in response to DisconnectFromViewModel being called.

		//	View.DataContextChanged -= View_DataContextChanged;
		//	View = null;
		//}

		//#if DEBUG
		//		~ViewHelper() {
		//			Debugger.Break(); // Oops. You must dispose the ViewHelper when DisconnectFromViewModel is called.
		//		}
		//#endif

		void View_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) {
			if (e.OldValue != null) {
				ViewModel.PropertyChanged -= ViewModel_PropertyChanged;
				View.OnViewModelRemoved();
				ViewModel = null;
			}

			// NOTE: There is a bug in WPF where e.NewValue is sometimes of type DisconnectedItem, a type that is internal to Microsoft.
			// See https://connect.microsoft.com/VisualStudio/feedback/details/619658/wpf-virtualized-control-disconnecteditem-reference-when-datacontext-switch?wa=wsignin1.0
			// We can work around it by detecting DisconnectedItem and treating it the same as a null data context.
			var newViewModel = (TViewModel)(View.DataContext != null && !(View.DataContext is TViewModel) ? null : View.DataContext);

			if (newViewModel != null) {
				ViewModel = newViewModel;
				ViewModel.PropertyChanged += ViewModel_PropertyChanged;
				View.OnViewModelApplied();
			}
		}

		void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) {
			View.OnPropertyChanged(e);
		}
	}
}
